package com.tianji.learning.service.impl;

import com.google.common.collect.Lists;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.vo.SignRecordVO;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.msg.SignInMessage;
import com.tianji.learning.service.ISignRecordsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class SignRecordsServiceImpl implements ISignRecordsService {

    private final StringRedisTemplate redisTemplate;

    private final RabbitMqHelper rabbitMqHelper;

    /**
     * 签到
     */
    @Override
    public SignResultVO addSignRecords() {
        //1.获取用户id
        Long userId = UserContext.getUser();
        //2.拼接key
        LocalDate now = LocalDate.now();
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId + format;
        //3.存到redis的bitMap中
        int offset = now.getDayOfMonth() - 1;
        Boolean result = redisTemplate.opsForValue().setBit(key, offset, true);
        //4.判断是否已签到 防止重复签到 1表示已签 0表示未签
        if (result) {
            throw new BizIllegalException("不能重复签到");
        }
        //5.计算连续签到天数
        int days = countSignDays(key, now.getDayOfMonth());
        int rewardPoints = 0;
        //6.计算积分
        switch (days) {
            case 7:
                rewardPoints = 10;
                break;
            case 14:
                rewardPoints = 20;
                break;
            case 28:
                rewardPoints = 40;
                break;
        }
        //7.发送消息，用于保存到积分记录表
        SignInMessage msg = SignInMessage.of(userId, rewardPoints + 1);//签到本身也加1分
        rabbitMqHelper.send(MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                msg
        );
        //8.封装vo
        SignResultVO signResultVO = new SignResultVO();
        signResultVO.setSignDays(days);
        signResultVO.setRewardPoints(rewardPoints);
        return signResultVO;
    }

    /**
     * 查询签到记录
     */
    @Override
    public List<Long> querySignRecords() {
        //1.获取用户id
        Long userId = UserContext.getUser();
        //2.拼接key
        LocalDate now = LocalDate.now();
        int dayOfMonth = now.getDayOfMonth();
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId + format;
        List<Long> bitField = redisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (CollUtils.isEmpty(bitField)) {
            throw new BizIllegalException("无签到记录");
        }

        Long num = bitField.get(0);
        System.out.println(num);

        List<Long> signList = new ArrayList<>();
        for (int i = 0; i < dayOfMonth; i++) {
            signList.add(num & 1);
            num = num >>> 1;
        }
        return CollUtils.reverse(signList);
    }

    /**
     * 计算连续签到天数 十进制转二进制后，和1与运算，然后右移
     */
    private int countSignDays(String key, int dayOfMonth) {
        int count = 0;
        //从第1天(下标0)到这个月的第几天
        List<Long> bitField = redisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (CollUtils.isEmpty(bitField)) {
            return 0;
        }
        long num = bitField.get(0);//只有一条数据，直接取
        log.debug("十进制num = {}", num);
        while ((num & 1) == 1) {//num&1会自动转为二进制进行与运算
            count++;
            num = num >>> 1;//右移一位
        }
        return count;
    }

}
